AWS再入門ブログリレー2022 Terraform Registry Module (AWS VPC Terraform module) 編
こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。
当エントリは弊社コンサルティング部による『AWS 再入門ブログリレー 2022』の 5日目のエントリです。
このブログリレーの企画は、普段 AWS サービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、 今一度初心に返って、基本的な部分を見つめ直してみよう解説してみようというコンセプトが含まれています。
AWSをこれから学ぼう!という方にとっては文字通りの入門記事として、またすでにAWSを活用されている方にとってもAWSサービスの再発見や2022年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。
では、さっそくいってみましょう。5日目のテーマは「Terraform Registry Module (AWS VPC Terraform module)編」です。
(AWS再入門といいつつTerraformもよく使うツールなのでご容赦ください。)
今回のブログでは、ハンズオンも実施しますのでぜひ一度やってみてください!
Registry Moduleを使いこなそう!
Terraformを使う上で、「Terraformでコードを1から作るのは大変だからRegistry Moduleを使おう!」と一度は、聞いたことがあるではないかと思います。
私はあります。
しかし、英語のドキュメント、分量も多くて必要な情報がわからない、そもそも使い方わからないとハードルが高いと私は思います。
少なからず、私は上記の壁にぶつかり続けた人間の1人です。
今回は、そんなRegistry Moduleを解説します。
そもそもRegistry Moduleとは
文字通り、Terraform Registryに登録されているモジュールです。
Registry Module一覧は、こちらから検索することで、モジュール一覧を参照できます。
今回は、AWS VPC Terraform moduleについて解説します。
モジュールの使い方
はじめに、ローカル上で作成した、モジュールの使い方をご説明します。
モジュールの作成方法
まず、3つのファイルを、同一フォルダ内に作成します。
※1つのファイルに、まとめることもできますが、便宜上分けています。
- variables.tf:
variable
ブロック(引数)を定義するファイル - main.tf:
resource
ブロックを定義するファイル - outputs.tf:
output
ブロック(戻り値)を定義するファイル
フォルダ階層のイメージは以下の通りです。
今回だと、module_name
フォルダ配下にまとめています。
|-modules # このフォルダも、好みで省略できますが、便宜上まとめています。
| |-module_name # VPCをつくるmoduleなら、「vpc」など好みで命名します。
| |-variables.tf
| |-main.tf
| |-outputs.tf
|
|-main.tf
|-variable.tf
|-terraform.tfvars
IAMユーザー(PowerUserAccess)を作成するモジュールを、定義してみようと思います。
variable "user_name" {
type = string
}
resource "aws_iam_user" "main" {
name = var.user_name
path = "/"
tags = {
"Name" = var.user_name
}
}
resource "aws_iam_user_policy_attachment" "main" {
user = aws_iam_user.main.name
policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}
output "arn" {
value = aws_iam_user.main.arn
description = "The ARN assigned by AWS for this user."
}
output "name" {
value = aws_iam_user.main.name
description = "The user's name."
}
output "tags_all" {
value = aws_iam_user.main.tags_all
description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block."
}
output "unique_id" {
value = aws_iam_user.main.unique_id
description = "The unique ID assigned by AWS."
}
|-modules
| |-power_user
| |-variables.tf
| |-main.tf
| |-outputs.tf
|
|-iam.tf
|-variable.tf
|-terraform.tfvars
モジュールを呼び出す方法
module "ローカル名" {
source = # モジュールの格納フォルダパスを定義
# モジュール内で定義されている引数を渡す
}
power_userモジュールを呼び出す場合
module "power_user" {
source = "../modules/power_user"
user_name = "terraform-reintroduction"
}
モジュールを呼び出す際は、terraform planやterraform apply実行前に、terraform getまたは、terraform initを実行する必要があります。
terraform getやterraform initについて
terraform getやterraform initでは、モジュールのファイルパスとリソース名のマッピングを行います。
マッピング情報は、.terraform/modules/modules.json
にあります。
|-modules
| |-power_user
| |-variables.tf
| |-main.tf
| |-outputs.tf
|
|-iam.tf
|-variable.tf
|-terraform.tfvars
|-.terraform
| |-modules
| | |-modules.json
| |-providers
{
"Modules":[
{
"Key":"",
"Source":"",
"Dir":"."
},
{
"Key":"power_user",
"Source":"../modules/power_user",
"Dir":"../modules/power_user"
}
]
}
terraform get
やterraform init
のタイミングについて
モジュール使用用途での実行のタイミングは、「新しくローカル名を定義した」または、「source(フォルダ階層)プロパティを変更した」場合に都度行います。
実行タイミングについて
- module "A"を定義しました:実行する
- module "A"の引数を変更しました:実行しない
- module "B"を定義しました:実行する
- module "A"を削除しました:実行しない
- module "A"を作成しました(
sourceプロパティ
変更無し):実行しない - module "A"の
sourceプロパティ
を変更しました:実行する - module "A"を削除しました:実行しない
- module "A"を定義しました(source変更):実行する
モジュールで作成したリソースの値を参照する方法
モジュールで作成されたリソースの値を、modules/power_user
フォルダ外部から参照する場合は、output
の値のみ参照可能です。
たとえば、さきほどのiam.tf
で、モジュールから作成したIAMユーザーを、IAMユーザーグループへ所属させる際は以下のように参照します。
module "power_user" {
source = "../modules/power_user"
user_name = "terraform-reintroduction"
}
resource "aws_iam_group" "group" {
name = "group-terraform-reintroduction"
path = "/"
}
resource "aws_iam_group_membership" "group" {
name = "group-membership-terraform-reintroduction"
group = aws_iam_group.group.name
users = [
module.power_user.name # ../modules/power_user/outputs.tfを参照。
# module.power_user.aws_iam_user.main.name のような参照はできない。
]
}
Registry Module 実践
さて、前置きがかなり長くなりましたが、ここからが本題です。
今回は、Terraform Registry Module界隈でもっとも使われているモジュールであろう、AWS VPC Terraform moduleについて、ハンズオンしてみようと思います。
AWS VPC Terraform moduleの使い方
まずは、AWS VPC Terraform moduleを開き、Provision Instructionsを見ます。
Registry Moduleとローカルのモジュールの圧倒的な違いは、version
を指定するか否かです。
Terraformでは、version
の指定が推奨されています。(ここ大事!)
The version argument is recommended for modules from a registry.
Module Blocks
バージョンを指定しない場合の挙動について
version
を指定しない場合、最新のバージョンがローカルの.terraform/modules/
に保存されます。
|-modules
| |-power_user
| |-variables.tf
| |-main.tf
| |-outputs.tf
|
|-iam.tf
|-variable.tf
|-terraform.tfvars
|-.terraform
| |-modules
| | |-modules.json
| | |-vpc
| | |- main.tf など
| |-providers
「terraform get
やterraform init
について」で、「モジュールの情報は、.terraform/modules/modules.json
に保存される」とご説明しました。
Terraform Registry Moduleも同じく、モジュールの情報は、.terraform/modules/modules.json
内に保存されます。
以下は、AWS VPC Terraform moduleを定義して、terraform getを行なった際のmodules.json
の中身です。
{
"Modules":[
{
"Key":"",
"Source":"",
"Dir":"."
},
{
"Key":"vpc",
"Source":"registry.terraform.io/terraform-aws-modules/vpc/aws",
"Version":"3.11.5",
"Dir":".terraform/modules/vpc"
}
]
}
version
プロパティを指定しない場合、次の事象が発生すると、偶発的な変更の恐れがあります。
- terraform init, apply(モジュール v3.11.5で、マッピング、リソース作成)
- モジュール v3.11.6以降がリリースされる
- modules.jsonが削除される
- 再度terraform init, apply(モジュール v3.11.6でマッピング、リソース作成)
- v3.11.5、v3.11.6間で何かしらの変更リスクが発生
バージョンを指定せず、modules.json
を削除してしまった場合、以前のバージョンからアップデートがあった場合、terraform get
やterraform init
でバージョンアップ(偶発的な変更)をしてしまいます。
そのため、Registry Moduleでは、バージョンアップによる偶発的な変更を防ぐために、version
プロパティを指定することが推奨されています。
心得その1 「version指定は行いましょう」
前述の通り、version指定を行わない場合偶発的なリソースへの変更リスクが生じてしまうため、Registry Moduleを使用する場合は、version
を指定します。
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.11.5"
# insert the 23 required variables here
}
心得その2 「とりあえずのterraform plan」
terraform initが終わったら、試しにterraform planをしてみましょう。
リソースは作られないので、とりあえずのterraform planです。
Terraformの原則であるWrite Plan Apply
に忠実に従いましょう。
今回の場合、terraform planを行うと、綺麗にエラーで怒られます。
% terraform plan
module.power_user.aws_iam_user.main: Refreshing state... [id=terraform-reintroduction]
module.power_user.aws_iam_user_policy_attachment.main: Refreshing state... [id=terraform-reintroduction-20220206090410142500000001]
╷
│ Error: expected "cidr_block" to contain a network Value with between 16 and 28 significant bits, got: 0
│
│ with module.vpc.aws_vpc.this[0],
│ on .terraform/modules/vpc/main.tf line 21, in resource "aws_vpc" "this":
│ 21: cidr_block = var.cidr
│
╵
エラーログから、variableのcidr
が適切な値(CIDR範囲が/16から/28)ではないことがわかります。
心得その3 「Inputsタブは惑わされるため、ReadmeタブのInputsを確認しよう!」
Inputsタブとは、Registry ModuleドキュメントのInputsタブを指します。
Inputsタブには、モジュールを使用するにあたり、引数として指定可能な値が記載されています。
AWS VPC Terraform moduleの場合、InputsはRequired Inputs、Optional Inputsの2つに分かれています。
Required Inputsでは、以下の説明書きがあります。
These variables must be set in the module block when using this module.
v3.11.5では、以下のInputsが、Required Inputsの対象です。
- database_subnet_assign_ipv6_address_on_creation
- database_subnet_group_name
- default_network_acl_name
- default_route_table_name
- default_security_group_name
- default_vpc_name
- elasticache_subnet_assign_ipv6_address_on_creation
- elasticache_subnet_group_name
- enable_classiclink
- enable_classiclink_dns_support
- flow_log_cloudwatch_log_group_kms_key_id
- flow_log_cloudwatch_log_group_retention_in_days
- flow_log_log_format
- intra_subnet_assign_ipv6_address_on_creation
- outpost_arn
- outpost_az
- outpost_subnet_assign_ipv6_address_on_creation
- private_subnet_assign_ipv6_address_on_creation
- public_subnet_assign_ipv6_address_on_creation
- redshift_subnet_assign_ipv6_address_on_creation
- redshift_subnet_group_name
- vpc_flow_log_permissions_boundary
- vpn_gateway_az
「いや、多すぎ!」って私は思います。安心してください。デフォルト値で入っています。
このInputsタブが、惑わされポイントなので注意しましょう。
では、引数のリファレンスは、どこを見ればいいのでしょうか。
答えは、「ReadmeタブのInputs」だと私は思います。
ReadmeタブのInputsとは
実は、ReadMeタブにもInputsの項目があります。
Inputsの名前、説明、データの型、デフォルト値、必須の有無が表形式で記載されています。
心得その2 「とりあえずのterraform plan」で怒られた、cidr
を確認してみましょう。
<blockquote>The CIDR block for the VPC. Default value is a valid CIDR, but not acceptable by AWS and should be overridden
DeepL翻訳:VPCのCIDRブロックです。デフォルト値は有効なCIDRですが、AWSでは受け入れられませんので、オーバーライドする必要があります。</blockquote>
<cite>cidr</cite>
前述のInputsタブにも記載されていますが、Required Inputsなど紛らわしい項目があるので個人的には、
- ReadmeタブのInputsで確認する
- わからない場合は、Inputsタブで確認する
の順番で、リファレンスの参照をオススメします。
引数cidr
を上書きして
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.11.5"
# insert the 23 required variables here
cidr = "10.0.0.0/16"
}
terraform planを実行すると、「VPCが作成されますよ!」と表示されました。
% terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.vpc.aws_vpc.this[0] will be created
+ resource "aws_vpc" "this" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = false
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = ""
}
+ tags_all = {
+ "Name" = (known after apply)
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
心得その4 「モジュールの理解はコードを少しだけ読むとこから」
心得その3 「Inputsタブは惑わされるため、ReadmeタブのInputsを確認しよう!」で実行したterraform planでは、作成されるVPCに対して、Name
タグが付与されていませんでした。
では、Nameタグを付与するには、どうすれば良いのでしょうか。
ReadmeタブのInputsでは、多くのInputsが定義されており本質にたどり着くには時間がかかります。
そこで、モジュールのコードを少しだけ確認します。
terraform initやterraform get後、Registry Moduleは.terraform/modules/
配下にダウンロードされます。
.terraform/modules/vpc/main.tf
を確認すると、作成されるVPCは以下のように定義されています。
resource "aws_vpc" "this" {
count = var.create_vpc ? 1 : 0
cidr_block = var.cidr
instance_tenancy = var.instance_tenancy
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
enable_classiclink = var.enable_classiclink
enable_classiclink_dns_support = var.enable_classiclink_dns_support
assign_generated_ipv6_cidr_block = var.enable_ipv6
tags = merge(
{ "Name" = var.name },
var.tags,
var.vpc_tags,
)
}
tags
を確認するとNameタグは、var.name
を参照していることがわかります。
つまり、モジュールを理解するには前後しますが、以下のステップが有効だということがわかります。
- ReadmeタブのInputsで確認する<span style="color: red">+αで実際のコードを少し読みInputを探す</span>
- わからない場合は、Inputsタブで確認する
コードを再度、書き換え
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.11.5"
# insert the 23 required variables here
cidr = "10.0.0.0/16"
name = "terraform-reintroduction"
}
terraform plan
を実行すると、Nameタグが変わっていることが確認できました。
% terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.vpc.aws_vpc.this[0] will be created
+ resource "aws_vpc" "this" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = false
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
Internet GatewayとPublicサブネットを作成しよう
VPC作成ときたらサブネットも作成したくなります。
ここでは、Internet Gatewayの作成、VPCへのアタッチ、2つのパブリックサブネットの作成、ルートテーブルの開通を行います。
前回に引き続き、コードを少しだけ読んでみましょう。
以下は、Internet Gatewayが定義されているコード部分になります。
resource "aws_internet_gateway" "this" {
count = var.create_vpc && var.create_igw && length(var.public_subnets) > 0 ? 1 : 0
vpc_id = local.vpc_id
tags = merge(
{ "Name" = var.name },
var.tags,
var.igw_tags,
)
}
count
を確認すると、create_vpc, create_igw, length(var.public_subnets)のすべてがtrue
の場合に、Internet Gatewayが作成されるようです。d
Inputsを確認します。
create_vpc, create_igwはデフォルトでtrue
となっているので、length(var.public_subnets)が1以上の場合に、Internet Gatewayは作成されるとわかります。
また、public_subnetsのデフォルトは[]
なので、デフォルトの引数値ではInternet Gatewayは作成されないことがわかります。
では、引数public_subnets
には何を入れれば良いのでしょうか。
Inputsのリファレンスを見ても検討がつきません。
A list of public subnets inside the VPC
そこで、再度コード内で、public_subnetsの参照を探します。
resource "aws_subnet" "public" {
count = var.create_vpc && length(var.public_subnets) > 0 && (false == var.one_nat_gateway_per_az || length(var.public_subnets) >= length(var.azs)) ? length(var.public_subnets) : 0
vpc_id = local.vpc_id
cidr_block = element(concat(var.public_subnets, [""]), count.index)
availability_zone = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) > 0 ? element(var.azs, count.index) : null
availability_zone_id = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) == 0 ? element(var.azs, count.index) : null
map_public_ip_on_launch = var.map_public_ip_on_launch
assign_ipv6_address_on_creation = var.public_subnet_assign_ipv6_address_on_creation == null ? var.assign_ipv6_address_on_creation : var.public_subnet_assign_ipv6_address_on_creation
ipv6_cidr_block = var.enable_ipv6 && length(var.public_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, var.public_subnet_ipv6_prefixes[count.index]) : null
tags = merge(
{
"Name" = format(
"${var.name}-${var.public_subnet_suffix}-%s",
element(var.azs, count.index),
)
},
var.tags,
var.public_subnet_tags,
)
}
cidr_block
プロパティにて、引数public_subnets
が使用されているため、public_subnetsにはパブリックサブネットのCIDRが入ることがわかります。
コードを書き換え
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.11.5"
# insert the 23 required variables here
cidr = "10.0.0.0/16"
name = "terraform-reintroduction"
public_subnets = ["10.0.0.0/24", "10.0.1.0/24"]
}
terraform planを実行すると、めっちゃ怒られます。
terraform plan
╷
│ Error: Error in function call
│
│ on .terraform/modules/vpc/main.tf line 355, in resource "aws_subnet" "public":
│ 355: availability_zone = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) > 0 ? element(var.azs, count.index) : null
│ ├────────────────
│ │ count.index is 0
│ │ var.azs is empty list of string
│
│ Call to function "element" failed: cannot use element function with an empty list.
╵
╷
│ Error: Error in function call
│
│ on .terraform/modules/vpc/main.tf line 355, in resource "aws_subnet" "public":
│ 355: availability_zone = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) > 0 ? element(var.azs, count.index) : null
│ ├────────────────
│ │ count.index is 1
│ │ var.azs is empty list of string
│
│ Call to function "element" failed: cannot use element function with an empty list.
╵
╷
│ Error: Error in function call
│
│ on .terraform/modules/vpc/main.tf line 356, in resource "aws_subnet" "public":
│ 356: availability_zone_id = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) == 0 ? element(var.azs, count.index) : null
│ ├────────────────
│ │ count.index is 0
│ │ var.azs is empty list of string
│
│ Call to function "element" failed: cannot use element function with an empty list.
╵
####### 省略 #######
理由としては、引数azs
が空のリストだからと確認できます。
A list of availability zones names or ids in the region
今回は、Inputsから理解できたのでそのままコードを修正します。
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.11.5"
# insert the 23 required variables here
cidr = "10.0.0.0/16"
name = "terraform-reintroduction"
public_subnets = ["10.0.0.0/24", "10.0.1.0/24"]
azs = ["ap-northeast-1a", "ap-northeast-1c"]
}
terraform planでInternet Gatewayの作成、VPCへのアタッチ、2つのパブリックサブネットの作成、ルートテーブルの開通を作成されることが確認できました。
% terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.vpc.aws_internet_gateway.this[0] will be created
+ resource "aws_internet_gateway" "this" {
+ arn = (known after apply)
+ id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_route.public_internet_gateway[0] will be created
+ resource "aws_route" "public_internet_gateway" {
+ destination_cidr_block = "0.0.0.0/0"
+ gateway_id = (known after apply)
+ id = (known after apply)
+ instance_id = (known after apply)
+ instance_owner_id = (known after apply)
+ network_interface_id = (known after apply)
+ origin = (known after apply)
+ route_table_id = (known after apply)
+ state = (known after apply)
+ timeouts {
+ create = "5m"
}
}
# module.vpc.aws_route_table.public[0] will be created
+ resource "aws_route_table" "public" {
+ arn = (known after apply)
+ id = (known after apply)
+ owner_id = (known after apply)
+ propagating_vgws = (known after apply)
+ route = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-public"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-public"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_route_table_association.public[0] will be created
+ resource "aws_route_table_association" "public" {
+ id = (known after apply)
+ route_table_id = (known after apply)
+ subnet_id = (known after apply)
}
# module.vpc.aws_route_table_association.public[1] will be created
+ resource "aws_route_table_association" "public" {
+ id = (known after apply)
+ route_table_id = (known after apply)
+ subnet_id = (known after apply)
}
# module.vpc.aws_subnet.public[0] will be created
+ resource "aws_subnet" "public" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.0.0.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_public_ip_on_launch = true
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-public-ap-northeast-1a"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-public-ap-northeast-1a"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_subnet.public[1] will be created
+ resource "aws_subnet" "public" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1c"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.0.1.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_public_ip_on_launch = true
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-public-ap-northeast-1c"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-public-ap-northeast-1c"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_vpc.this[0] will be created
+ resource "aws_vpc" "this" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = false
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction"
}
}
Plan: 8 to add, 0 to change, 0 to destroy.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
心得その5 「命名規則は、ローカルで変更しちゃえ!」
作成されるVPC、サブネットのNameタグが、企業の命名規則に、即してないケースもあるのではないでしょうか。
そのような場合は、ローカルにて保存してNameタグ部分を書き換えてしまいましょう。
まずは、.terraform/modules/vpc
をコピーします。
|-modules
| |-power_user
| | |-variables.tf
| | |-main.tf
| | |-outputs.tf
| |-vpc <-----------|
| |
|-iam.tf |
|-vpc.tf |
|-variable.tf |
|-terraform.tfvars |
|-.terraform |
| |-modules |
| | |-modules.json |
| | |-vpc ---------|
| |-providers
次にmodule呼び出すコードを書き換えます。
module "vpc" {
source = "../modules/vpc" # モジュールを読み込むファイルパスを変更
# version = "3.11.5" # ローカルモジュールでは、version指定しないためコメントアウト
# insert the 23 required variables here
cidr = "10.0.0.0/16"
name = "terraform-reintroduction"
public_subnets = ["10.0.0.0/24", "10.0.1.0/24"]
azs = ["ap-northeast-1a", "ap-northeast-1c"]
}
terraform initまたは、terraform getでマッピングを変更します。
% terraform init
Initializing modules...
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v3.74.0
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
モジュールのマッピング変更が完了しました。
次に、ローカルモジュールのコードを書き換えます。
################################################################################
# VPC
################################################################################
resource "aws_vpc" "this" {
count = var.create_vpc ? 1 : 0
cidr_block = var.cidr
instance_tenancy = var.instance_tenancy
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
enable_classiclink = var.enable_classiclink
enable_classiclink_dns_support = var.enable_classiclink_dns_support
assign_generated_ipv6_cidr_block = var.enable_ipv6
tags = merge(
{ "Name" = "${var.name}-vpc" }, # { "Name" = var.name }
var.tags,
var.vpc_tags,
)
}
### 省略 ###
################################################################################
# Internet Gateway
################################################################################
resource "aws_internet_gateway" "this" {
count = var.create_vpc && var.create_igw && length(var.public_subnets) > 0 ? 1 : 0
vpc_id = local.vpc_id
tags = merge(
{ "Name" = "${var.name}-igw" }, # { "Name" = var.name }
var.tags,
var.igw_tags,
)
}
### 省略 ###
################################################################################
# Publiс routes
################################################################################
resource "aws_route_table" "public" {
count = var.create_vpc && length(var.public_subnets) > 0 ? 1 : 0
vpc_id = local.vpc_id
tags = merge(
{ "Name" = "${var.name}-${var.public_subnet_suffix}-rtb" }, # { "Name" = "${var.name}-${var.public_subnet_suffix}" }
var.tags,
var.public_route_table_tags,
)
}
### 省略
################################################################################
# Public subnet
################################################################################
resource "aws_subnet" "public" {
count = var.create_vpc && length(var.public_subnets) > 0 && (false == var.one_nat_gateway_per_az || length(var.public_subnets) >= length(var.azs)) ? length(var.public_subnets) : 0
vpc_id = local.vpc_id
cidr_block = element(concat(var.public_subnets, [""]), count.index)
availability_zone = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) > 0 ? element(var.azs, count.index) : null
availability_zone_id = length(regexall("^[a-z]{2}-", element(var.azs, count.index))) == 0 ? element(var.azs, count.index) : null
map_public_ip_on_launch = var.map_public_ip_on_launch
assign_ipv6_address_on_creation = var.public_subnet_assign_ipv6_address_on_creation == null ? var.assign_ipv6_address_on_creation : var.public_subnet_assign_ipv6_address_on_creation
ipv6_cidr_block = var.enable_ipv6 && length(var.public_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, var.public_subnet_ipv6_prefixes[count.index]) : null
tags = merge(
{
"Name" = format(
"%s-subnet-${var.public_subnet_suffix}-%s",
var.name,
substr(var.azs[count.index], -2, -1),
)
},
/*
{
"Name" = format(
"${var.name}-${var.public_subnet_suffix}-%s",
element(var.azs, count.index),
)
},
*/
var.tags,
var.public_subnet_tags,
)
}
ローカルで、モジュールを管理することによって、命名規則を自由に変更することができました。
しかし、「ローカルでモジュールを管理するということは、バージョンアップのメンテナンスは自身で行うこと」を意味します。
定期的にモジュールの変更ログを確認する必要があることに注意してください。
% terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.vpc.aws_internet_gateway.this[0] will be created
+ resource "aws_internet_gateway" "this" {
+ arn = (known after apply)
+ id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-igw"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-igw"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_route.public_internet_gateway[0] will be created
+ resource "aws_route" "public_internet_gateway" {
+ destination_cidr_block = "0.0.0.0/0"
+ gateway_id = (known after apply)
+ id = (known after apply)
+ instance_id = (known after apply)
+ instance_owner_id = (known after apply)
+ network_interface_id = (known after apply)
+ origin = (known after apply)
+ route_table_id = (known after apply)
+ state = (known after apply)
+ timeouts {
+ create = "5m"
}
}
# module.vpc.aws_route_table.public[0] will be created
+ resource "aws_route_table" "public" {
+ arn = (known after apply)
+ id = (known after apply)
+ owner_id = (known after apply)
+ propagating_vgws = (known after apply)
+ route = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-public-rtb"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-public-rtb"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_route_table_association.public[0] will be created
+ resource "aws_route_table_association" "public" {
+ id = (known after apply)
+ route_table_id = (known after apply)
+ subnet_id = (known after apply)
}
# module.vpc.aws_route_table_association.public[1] will be created
+ resource "aws_route_table_association" "public" {
+ id = (known after apply)
+ route_table_id = (known after apply)
+ subnet_id = (known after apply)
}
# module.vpc.aws_subnet.public[0] will be created
+ resource "aws_subnet" "public" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.0.0.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_public_ip_on_launch = true
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-subnet-public-1a"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-subnet-public-1a"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_subnet.public[1] will be created
+ resource "aws_subnet" "public" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1c"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.0.1.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_public_ip_on_launch = true
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-subnet-public-1c"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-subnet-public-1c"
}
+ vpc_id = (known after apply)
}
# module.vpc.aws_vpc.this[0] will be created
+ resource "aws_vpc" "this" {
+ arn = (known after apply)
+ assign_generated_ipv6_cidr_block = false
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = false
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "terraform-reintroduction-vpc"
}
+ tags_all = {
+ "Name" = "terraform-reintroduction-vpc"
}
}
Plan: 8 to add, 0 to change, 0 to destroy.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
最後に
以上、『AWS 再入門ブログリレー 2022』の 5日目のエントリ『Terraform Registry Module (AWS VPC Terraform module)』編でした。
Terraform Registry Module使ってみたいけど使い方わからない人に向けて参考になればとても嬉しいです。
明日、2/8 火曜日は、べこみんさんの「CloudShell編」の予定です!
以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!